package org.realogic.btp.resourcefile;

import java.io.File;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.realogic.cfm.util.UtilString;

public class KResourceFileFinder {

	private boolean recursive;
	private LinkedList<File> fileList;
	private boolean includeDirs = true;
	private boolean includeFiles = true;
	private boolean listSubfilesAfterFolder = true;
	private Pattern fileNnamePattern;

	public KResourceFileFinder() {

	}

	/**
	 * Activates recursive search.
	 */
	public KResourceFileFinder setRecursive(boolean recursive) {
		this.recursive = recursive;
		return this;
	}

	public KResourceFileFinder setIncludeDirs(boolean includeDirs) {
		this.includeDirs = includeDirs;
		return this;
	}

	public KResourceFileFinder setFileNnamePattern(Pattern aFileNnamePattern) {
		this.fileNnamePattern = aFileNnamePattern;
		return this;
	}

	public boolean isRecursive() {
		return recursive;
	}

	public boolean isIncludeDirs() {
		return includeDirs;
	}

	public boolean isIncludeFiles() {
		return includeFiles;
	}

	public KResourceFileFinder setIncludeFiles(boolean includeFiles) {
		this.includeFiles = includeFiles;
		return this;
	}

	protected void addSearchPath(File searchPath) {
		if (searchPath.exists() == false) {
			return;
		}
		if (fileList == null) {
			fileList = new LinkedList<File>();
		}
		if (searchPath.isDirectory() == false) {
			if (fileNnamePattern != null) {
				Matcher matcher = fileNnamePattern.matcher(searchPath.getName());
				if (matcher.matches()) {
					fileList.add(searchPath);
				}
			} else {
				fileList.add(searchPath);
			}
			return;
		}
		listFiles(searchPath);
	}

	public KResourceFileFinder searchPath(File searchPath) {
		addSearchPath(searchPath);
		return this;
	}

	public KResourceFileFinder searchPath(File... searchPath) {
		for (File file : searchPath) {
			addSearchPath(file);
		}
		return this;
	}

	public KResourceFileFinder searchPath(String searchPath) {
		if (searchPath.indexOf(File.pathSeparatorChar) != -1) {
			String[] paths = UtilString.tokenize(searchPath, File.pathSeparator);
			for (String path : paths) {
				addSearchPath(new File(path));
			}
			return this;
		}
		addSearchPath(new File(searchPath));
		return this;
	}

	public KResourceFileFinder searchPath(String... searchPaths) {
		for (String searchPath : searchPaths) {
			searchPath(searchPath);
		}
		return this;
	}

	public boolean isListSubfilesAfterFolder() {
		return listSubfilesAfterFolder;
	}

	public KResourceFileFinder setListSubfilesAfterFolder(boolean listSubfilesAfterFolder) {
		this.listSubfilesAfterFolder = listSubfilesAfterFolder;
		return this;
	}

	public File nextFile() {
		if (fileList == null) {
			return null;
		}
		while (true) {
			if (fileList.isEmpty()) {
				fileList = null;
				return null;
			}
			File currentFile = fileList.removeFirst();
			if (currentFile.isDirectory()) {
				if (recursive == true) {
					listFiles(currentFile);
				}
				if (includeDirs == true) {
					if (acceptFile(currentFile) == true) {
						return currentFile;
					}
				}
				continue;
			}
			return currentFile;
		}
	}

	public Iterator<File> iterator() {
		return new Iterator<File>() {
			private File nextFile;

			public boolean hasNext() {
				nextFile = nextFile();
				return nextFile != null;
			}

			public File next() {
				if (nextFile == null) {
					throw new NoSuchElementException();
				}
				return nextFile;
			}

			public void remove() {
				throw new UnsupportedOperationException();
			}
		};
	}

	protected void listFiles(File directory) {
		File[] list = directory.listFiles();
		LinkedList<File> subFolders;
		LinkedList<File> subFiles;
		if (listSubfilesAfterFolder == false) {
			subFolders = fileList;
			subFiles = fileList;
		} else {
			subFolders = new LinkedList<File>();
			subFiles = new LinkedList<File>();
		}
		for (File currentFile : list) {
			if (currentFile.isFile() == true) {
				if ((includeFiles == true) && (acceptFile(currentFile) == true)) {
					Matcher matcher = fileNnamePattern.matcher(currentFile.getName());
					if (matcher.matches()) {
						subFiles.addLast(currentFile);
					}
				}
			} else if (currentFile.isDirectory() == true) {
				subFolders.addLast(currentFile);
			}
		}
		if (listSubfilesAfterFolder == true) {
			if (subFiles.isEmpty() == false) {
				fileList.addAll(0, subFiles);
			}
			if (subFolders.isEmpty() == false) {
				fileList.addAll(0, subFolders);
			}
		}
	}

	protected boolean acceptFile(File currentFile) {
		return true;
	}

}
